﻿//===============================================================================
// Microsoft Innovation Centre - Western Europe
// Copyright (c) 2008 Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Controls;
using System.Windows.Media;
using Microsoft.WVB.Framework.AdControls;
using Microsoft.WVB.Silverlight.Infrastructure;
using Microsoft.Practices.Composite.Events;
using Microsoft.Practices.Composite.Wpf.Events;

namespace Microsoft.WVB.Framework
{
    /// <summary>
    /// That is an option that will then use our Mediator Message Bus.
    /// </summary>
    public class AdManager : BaseController
    {
        public static TimeSpan BufferingTime = TimeSpan.FromSeconds(2);
        public const string AdMarkerType = "Ad";
        public const string BufferMarkerType = "Buffer";

        private Dictionary<TimeSpan, AdContext> AdContexts = null;
        private Dictionary<AdVisual, Panel> AdSurfaces = null;
        private List<AdLayoutBase> AdLayouts = null;
        private MediaElement cacheMediaRef = null;

        public AdContext CurrentContext
        {
            get { return _CurrentContext; }
            set { _CurrentContext = value; OnPropertyChanged("CurrentContext"); }
        }
        private AdContext _CurrentContext = null;

        public TimeSpan CurrentPlaying
        {
            get { return _CurrentPlaying; }
            set { _CurrentPlaying = value; OnPropertyChanged("CurrentPlaying"); }
        }
        private TimeSpan _CurrentPlaying;

        public int SessionId { get; private set; }

        public AdManager()
        {
            AdLayouts = new List<AdLayoutBase>();
            AdContexts = new Dictionary<TimeSpan, AdContext>();
            AdSurfaces = new Dictionary<AdVisual, Panel>();
            Random rd = new Random();
            SessionId = rd.Next(1000);

            IEventAggregator ag = IoC.GetCreate<IEventAggregator>();
            ag.GetEvent<MediaOpenedEvent>().Subscribe(AcquireMediaContext, ThreadOption.UIThread, true);
        }

        private void ResetAdLayouts()
        {
            foreach (AdLayoutBase item in AdLayouts)
            {
                item.OnEndShowAd();
            }
        }

        /// <summary>
        /// This method is called by the AdSupportService attached property in order to register a Panel
        /// as a Ad surface. Therefore, a ControlBaseAd derived control is added as a child to the Panel
        /// and can display Ads.
        /// </summary>
        /// <param name="adVisual"></param>
        /// <param name="panel"></param>
        public void RegisterAdDisplaySurface(AdVisual adVisual, Panel panel)
        {
            if (AdSurfaces.ContainsKey(adVisual))
            {
                throw new ArgumentOutOfRangeException("there must be only ONE type of Ad support participant Control for the entire application");
            }
            else
            {
                if (adVisual == AdVisual.Overlay)
                {
                    AdOverlay ad = new AdOverlay();
                    AdLayouts.Add(ad);
                    ad.SetValue(Canvas.ZIndexProperty, 100);
                 //   ad.Width = panel.Width;
                  //  ad.Height = panel.Height;
                    panel.Children.Add(ad);
                }
                else
                {
                    AdBanner ad = new AdBanner();
                    AdLayouts.Add(ad);
                    ad.SetValue(Canvas.ZIndexProperty, 100);
                 //   ad.Width = panel.Width;
                 //   ad.Height = panel.Height;
                    panel.Children.Add(ad);
                }

                AdSurfaces.Add(adVisual, panel);
            }
        }

        /// <summary>
        /// This method is called by the MediaPlayer Model either through Mediator - then we need to decorate
        /// or directly through a reference of the AdManager object. This should only happen after the 
        /// Media.Opened event has occurred since Markers are not available before that.
        /// </summary>
        /// <param name="media"></param>
        /// 
        //[MediatorEventHandler(Events.MessageTopics.MediaOpened)]
        public void AcquireMediaContext(MediaElement media)
        {
            List<TimelineMarker> bufferMarkers = new List<TimelineMarker>();
            
            // Video changes - so the context needs to be cleared.
            ResetAdLayouts();
            AdContexts.Clear();

            // loop into the TimelineMarker to discover where Ads have been tagged into the video
            // We need to get those tags and save the Keyworks associated with the Ad.
            // Also, we need to be aware that Video needs buffering so we had new buffering TimelineMarkers
            // ahead of the Ad TimelineMarker of the BufferingTime static property and add them to the Media Element 
            // so the MediaElement will notify the AdManager that Something happened.
            foreach (TimelineMarker marker in media.Markers)
            {
                if (marker.Type == AdManager.AdMarkerType)
                {
                    TimelineMarker m = new TimelineMarker();
                    m.Type = AdManager.BufferMarkerType;
                    m.Text = marker.Time.ToString();
                    m.Time = marker.Time.Subtract(BufferingTime);

                    AdContext adCtx = new AdContext(marker.Time);
                    adCtx.AdKeywords = marker.Text;

                    AdContexts.Add(marker.Time, adCtx);
                    bufferMarkers.Add(m);
                }
            }

            // can't modify a collection when iterating through it.
            foreach (TimelineMarker m in bufferMarkers)
            {
                media.Markers.Add(m);
#if TRACEDEBUG
                Debug.WriteLine("Buffer Marker @ " + m.Time.ToString());
#endif
            }

            // directly connect to the Media ... we can change this later on if we decide to not have
            // the AdManager doing the all work and use the Mediator.
            // We need to know if this is the same Media ... normally, this is the same ... so no need
            // to add more than one handle, if not doing the test, everytime you change the source, 
            // a new handler will be added to the list and the code will be executed more than once!
            if (!media.Equals(this.cacheMediaRef))
            {
                this.cacheMediaRef = media;
                media.MarkerReached += delegate(object sender, TimelineMarkerRoutedEventArgs e)
                {
                    EventHandler<AcquireCompletedEventArgs<AdContext>> handler = null;

                    if (e.Marker.Type == AdManager.BufferMarkerType)
                    {
                        // Buffer the Ad

#if TRACEDEBUG
                        Debug.WriteLine("Buffering Ad " + e.Marker.Time.ToString());
#endif
                        TimeSpan ts = TimeSpan.Parse(e.Marker.Text);
                        AdProvider adProvider = IoC.GetCreate<AdProvider>();
                        IUser user = IoC.GetCreate<IUser>();
                        AdProviderContext adProviderCtx = new AdProviderContext();

                        adProviderCtx.Profile = user.ProfileInfo;
                        adProviderCtx.Context = AdContexts[ts];

                        if (handler == null)
                        {
                            handler = delegate(object s, AcquireCompletedEventArgs<AdContext> ev)
                            {
                                TimeSpan index = (TimeSpan)ev.UserState;
#if TRACEDEBUG
                                Debug.Assert((ev.Result != null));
#endif
                                AdContexts[index] = ev.Result;
                                CurrentContext = AdContexts[index];

                                adProvider.GetAdCompleted -= handler;
                                handler = null;
                            };
                        }

                        adProvider.GetAdCompleted += handler;
                        adProvider.GetAdAsync(adProviderCtx, ts);
                    }
                    else if (e.Marker.Type == AdManager.AdMarkerType)
                    {
                        //Ready for playing
#if TRACEDEBUG
                        Debug.WriteLine("Playing Ad @ " + e.Marker.Time.ToString());
#endif
                        if (AdContexts[e.Marker.Time].IsProvisioned)
                            CurrentPlaying = e.Marker.Time;
                    }
                };
            }
        }
    }
}
